home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
floatpak.arc
/
FLOATPAK.TXT
Wrap
Text File
|
1988-09-18
|
21KB
|
964 lines
/*
** Floating-point library package.
** R.E. Grehan - BYTE Magazine
**
** What you've got here is a floating-point four-banger for the
** 8088/80286/80386 family. The routines perform:
** fpadd -- addition
** fpmult - multiplication
** fpsub -- subtraction
** fpdiv -- division
** fpinput - input (converts string to floating-point)
** fpoutput - output (converts floating-point to string)
**
** This is not an "industrial strength" package, at least
** not in terms of optimization (although I have unwound
** a lot of loops).
** The C interface routines are written (of course) for BYTE
** Small C -- you should be able to use them as interfacing
** guides for the actual assembly-language floating-point
** routines.
** In the input and output routines, I've added comments in
** spots where the code could be modified to allow for freer
** formats (like, no preceding '+' sign, for example).
** See my articles in the September and October 1988 BYTEs
** for more details.
** Enjoy.
** --Rick Grehan
**
** REFERENCES:
** Microprocessor Programming for Computer Hobbyists by Neill Graham
** Tab books, 1977
**
** Scelbi 8080 Software Gourmet Guide and Cook Book by Robert Findley
** Scelbi Computer Consulting, 1976
*/
/*
** Add two floating-point numbers.
** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
** numbers.
** This function performs: flt1+flt2 --> flt3.
** The function returns 0 if ok, else error code.
*/
fpadd(flt1,flt2,flt3)
char flt1[10], flt2[10], flt3[10];
{
#asm
MOV BP,SP
;First move flt1 and flt2 into FAC1 and FAC2
MOV SI,6[BP]
MOV DI,OFFSET FAC1_SIGN
CALL LDFACC
MOV SI,4[BP]
MOV DI,OFFSET FAC2_SIGN
CALL LDFACC
;Perform the addition
CALL FPADD
;Make sure everything was ok.
OR BX,BX
JZ _FPADD1
XOR CX,CX ;For small C
RET
;Return the result
_FPADD1:
MOV DI,2[BP]
CALL STFAC1
XOR CX,CX ;For small C
#endasm
}
/*
** Multiply two floating-point numbers.
** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
** numbers.
** This function performs: flt1*flt2 --> flt3.
** The function returns 0 if ok, else error code.
*/
fpmult(flt1,flt2,flt3)
char flt1[10], flt2[10], flt3[10];
{
#asm
MOV BP,SP
;First move flt1 and flt2 into FAC1 and FAC2
MOV SI,6[BP]
MOV DI,OFFSET FAC1_SIGN
CALL LDFACC
MOV SI,4[BP]
MOV DI,OFFSET FAC2_SIGN
CALL LDFACC
;Perform the multiplication
CALL FPMULT
;Make sure everything was ok.
OR BX,BX
JZ _FPMUL1
XOR CX,CX ;For small C
RET
;Return the result
_FPMUL1:
MOV DI,2[BP]
CALL STFAC1
XOR CX,CX ;For small C
#endasm
}
/*
** Subtract two floating-point numbers.
** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
** numbers.
** This function performs: flt1-flt2 --> flt3.
** The function returns 0 if ok, else error code.
*/
fpsub(flt1,flt2,flt3)
char flt1[10], flt2[10], flt3[10];
{
#asm
MOV BP,SP
;First move flt1 and flt2 into FAC1 and FAC2
MOV SI,6[BP]
MOV DI,OFFSET FAC1_SIGN
CALL LDFACC
MOV SI,4[BP]
MOV DI,OFFSET FAC2_SIGN
CALL LDFACC
;Perform the subtraction
CALL FPSUB
;Make sure everything was ok.
OR BX,BX
JZ _FPSUB1
XOR CX,CX ;For small c
RET
;Return the result
_FPSUB1:
MOV DI,2[BP]
CALL STFAC1
XOR CX,CX ;For small c
#endasm
}
/*
** Divide floating-point numbers.
** flt1, flt2, and flt3 are 10-byte arrays holding floating-point
** numbers.
** This function performs: flt1/flt2 --> flt3.
** The function returns 0 if ok, else error code.
*/
fpdiv(flt1,flt2,flt3)
char flt1[10], flt2[10], flt3[10];
{
#asm
MOV BP,SP
;First move flt1 and flt2 into FAC1 and FAC2
MOV SI,6[BP]
MOV DI,OFFSET FAC1_SIGN
CALL LDFACC
MOV SI,4[BP]
MOV DI,OFFSET FAC2_SIGN
CALL LDFACC
;Perform the division
CALL FPDIV
;Make sure everything was ok.
OR BX,BX
JZ _FPDIV1
XOR CX,CX ;For small c
RET
;Return the result
_FPDIV1:
MOV DI,2[BP]
CALL STFAC1
XOR CX,CX ;For small c
#endasm
}
/*
** Input a floating-point number.
** Accepts pointer to a string that must be in the
** form: sd.ddddEsdd<null>
** Also accepts pointer to an array of 10 bytes, into which
** the floating-point number is stored.
*/
fpinput(str,fnum)
char *str;
char fnum[10];
{
#asm
MOV BP,SP
;Put pointer to string in SI and call conversion routine
MOV SI,4[BP]
CALL FPINP
;Move the result into fnum
MOV DI,2[BP]
CALL STFAC1
XOR CX,CX ;For small c
#endasm
}
/*
** Output a floating-point number.
** Accepts pointer to a 10-byte array containing an floating-
** point number. Also accepts pointer to a buffer, into
** which the number is stored in the format:
** sd.ddddEsdd<null>
** Number of digits to the right of the decimal place is
** specified in n.
*/
fpoutput(fnum,buff,n)
char fnum[10];
char *buff;
int n;
{
#asm
MOV BP,SP
;First move the number into FAC1
MOV SI,6[BP]
MOV DI,OFFSET FAC1_SIGN
CALL LDFACC
;Do output
MOV CX,2[BP]
MOV DI,4[BP]
CALL FPOUT
XOR CX,CX ;For small c
#endasm
}
/*
** Actual floating-point routines follow.
*/
#asm
;
; Definitions for the floating-point accumulators
;
FAC1_SIGN DB ? ;FP ACCUM1 - SIGN (80h=negative, 0=positive)
FAC1_EXP DW ? ; - EXPONENT
FAC1_MAN DW 5 DUP (?) ; - MANTISSA
FAC2_SIGN DB ? ;Accumulator 2
FAC2_EXP DW ?
FAC2_MAN DW 5 DUP (?)
;Third accumulator for mantissa only - used as a temp location for
;calculations.
FAC3_MAN DW 5 DUP (?)
EXP_SIGN DB ? ;Sign of decimal exponent
;Equates
BIAS EQU 16384 ;Bias for exponent
MAXEXP EQU 32768 ;Maximum exponent
;Error codes returned
DIVZER EQU 1 ;Divide by zero error
EXPOVF EQU 2 ;Exponent overflow error
EXPUND EQU 3 ;Exponent underflow error
;**********
;Load a floating-point accumulator.
;Assumes SI points to a floating-point number and DI points
;to the _SIGN field of the accumulator to load.
PUBLIC LDFACC
LDFACC:
CLD ;Set direction to increment
;Get the sign bit
MOV AX,[SI]
AND AH,80H
MOV [DI],AH
INC DI
;Move the rest
MOV BX,DI ;Save destination location
MOV CX,5
REP MOVSW
MOV WORD PTR [DI],0
;Clear sign bit from exponent
AND WORD PTR [BX],7FFFH
INC BX
INC BX
;Shift the mantissa right 1 bit
SHR WORD PTR [BX],1
RCR WORD PTR 2[BX],1
RCR WORD PTR 4[BX],1
RCR WORD PTR 6[BX],1
RCR WORD PTR 8[BX],1
LDFACC1:
RET
;**********
;Store FACC1 to the memory location
;pointed to by DI.
;NOTE: THIS ROUTINE MUNGES FAC1 IN THE PROCESS
; OF STORING IT TO MEMORY. NOT A GOOD IDEA,
; PARTICULARLY IF YOU'RE DOING LOT'S OF BACK-
; TO-BACK COMPUTATIONS AND YOU WANT TO
; OPTIMIZE THINGS. (I just thought I should
; bring this to your attention.--rg)
PUBLIC STFAC1
STFAC1:
;Set direction
CLD
;Shift the mantissa left 1 bit
SHL FAC1_MAN+8,1
RCL FAC1_MAN+6,1
RCL FAC1_MAN+4,1
RCL FAC1_MAN+2,1
RCL FAC1_MAN,1
;Set the sign bit based on FAC1_SIGN
CMP FAC1_SIGN,0
JNE STFC1
AND FAC1_EXP,7FFFH
JMP SHORT STFC2
STFC1: OR FAC1_EXP,8000H
STFC2:
;Move it all over
MOV SI,OFFSET FAC1_EXP
MOV CX,5
REP MOVSW
RET
;**********
;Floating-point add routine
;Arguments assumed to be in FAC1
; and FAC2. Result in FAC1
PUBLIC FPADD
FPADD:
;Determine which argument has the
;larger exponent. Move the number with the
;larger exponent into FAC1 and the number with the
;smaller exponent into FAC2.
MOV AX,FAC1_EXP
CMP AX,FAC2_EXP
JAE FADD1
MOV CX,11
MOV SI,OFFSET FAC1_SIGN
MOV DI,OFFSET FAC2_SIGN
FADD0: MOV AL,[SI]
XCHG AL,[DI]
MOV [SI],AL
INC SI
INC DI
LOOP FADD0
;Now figure out how many bits to
;shift the mantissa of FAC2 in order
;to align the radix point. If this
;amount is greater than 78, we can
;go home, cause the result is already
;in FAC1.
FADD1: MOV AX,FAC1_EXP
SUB AX,FAC2_EXP
CMP AX,78
JL FADD2
XOR BX,BX ;Show all ok.
RET
;See if the signs of FAC1 and
;FAC2 differ. If so, negate the
;mantissa of FAC2.
FADD2: MOV BL,FAC1_SIGN
CMP BL,FAC2_SIGN
JZ FADD3
MOV BX,OFFSET FAC2_MAN
CALL NEGBX
;Now shift FAC2 right by amount
;given in AX (which is the difference in the
;exponent amounts).
FADD3: MOV CX,AX
OR CX,CX ;Shift necessary?
JZ FADD4A
FADD4: SHR FAC2_MAN,1
RCR FAC2_MAN+2,1
RCR FAC2_MAN+4,1
RCR FAC2_MAN+6,1
RCR FAC2_MAN+8,1
LOOP FADD4
;Add the two mantissas.
FADD4A:
MOV AX,FAC2_MAN+8
ADD FAC1_MAN+8,AX
MOV AX,FAC2_MAN+6
ADC FAC1_MAN+6,AX
MOV AX,FAC2_MAN+4
ADC FAC1_MAN+4,AX
MOV AX,FAC2_MAN+2
ADC FAC1_MAN+2,AX
MOV AX,FAC2_MAN
ADC FAC1_MAN,AX
;See if the sign of the two operands
;differ. If so, and if the result
;of the addition was negative, negate
;the FAC1 mantissa and move the sign
;of FAC2 to FAC1.
MOV AL,FAC1_SIGN
CMP AL,FAC2_SIGN
JZ FADD5
MOV AX,FAC1_MAN
OR AX,AX
JNS FADD5
MOV BX,OFFSET FAC1_MAN
CALL NEGBX
MOV AL,FAC2_SIGN
MOV FAC1_SIGN,AL
;Now normalize the contents of FAC1
FADD5: JMP NORM_FAC1
;**********
;Floating-point subtract.
;Subtract contents of FAC2 from
;FAC1 and leave the result in FAC1.
;
PUBLIC FPSUB
FPSUB:
;Flip the sign of FAC2
XOR FAC2_SIGN,80H
;And do an ADD
JMP FPADD
;**********
;Floating-point multiply
;Multiply FAC1 and FAC2 - result in FAC1
PUBLIC FPMULT
FPMULT:
;First set up the signs of the result. Do this by
;performing an exclusive-OR on the signs of the operands
MOV AL,FAC2_SIGN
XOR FAC1_SIGN,AL
;Now calculate the exponent of the result
MOV AX,FAC2_EXP
ADD AX,FAC1_EXP
SUB AX,BIAS+1
MOV FAC1_EXP,AX
;Clear FAC3 to hold results
XOR AX,AX
MOV FAC3_MAN,AX
MOV FAC3_MAN+2,AX
MOV FAC3_MAN+4,AX
MOV FAC3_MAN+6,AX
MOV FAC3_MAN+8,AX
;Set up counter
MOV CX,79
;Shift FAC1 and result 1 bit to the right.
;If the bit shifted out of FAC1 is a 1, add FAC2
;to the result.
FMUL1: SHR FAC3_MAN,1
RCR FAC3_MAN+2,1
RCR FAC3_MAN+4,1
RCR FAC3_MAN+6,1
RCR FAC3_MAN+8,1
;
SHR FAC1_MAN,1
RCR FAC1_MAN+2,1
RCR FAC1_MAN+4,1
RCR FAC1_MAN+6,1
RCR FAC1_MAN+8,1
JNC FMUL2
MOV AX,FAC2_MAN+8
ADD FAC3_MAN+8,AX
MOV AX,FAC2_MAN+6
ADC FAC3_MAN+6,AX
MOV AX,FAC2_MAN+4
ADC FAC3_MAN+4,AX
MOV AX,FAC2_MAN+2
ADC FAC3_MAN+2,AX
MOV AX,FAC2_MAN
ADC FAC3_MAN,AX
FMUL2: LOOP FMUL1
;Move the result into FAC1
FMUL3: MOV SI,OFFSET FAC3_MAN
MOV DI,OFFSET FAC1_MAN
MOV CX,5
REP MOVSW
;Exit via the normalization routine
JMP NORM_FAC1
;**********
;Floating-point divide
;Divide FAC1 by FAC2 - result in FAC1
PUBLIC FPDIV
FPDIV:
;Verify that we are not trying to divide by zero.
;Do this by checking the exponent of FAC2.
MOV AX,FAC2_EXP
OR AX,AX
JNE FDIV0
MOV BX,DIVZER ;Indicate error
RET ;Go home
;Figure out the sign of the result
FDIV0: MOV AL,FAC2_SIGN
XOR FAC1_SIGN,AL
;And calculate the resulting exponent
MOV AX,FAC1_EXP
SUB AX,FAC2_EXP
ADD AX,BIAS ;Fix bias
MOV FAC1_EXP,AX
;Now perform the division, using nonrestoring fraction
;division. First, clear the result.
XOR AX,AX
MOV FAC3_MAN,AX
MOV FAC3_MAN+2,AX
MOV FAC3_MAN+4,AX
MOV FAC3_MAN+6,AX
MOV FAC3_MAN+8,AX
;Do an initial subtraction
MOV AX,FAC2_MAN+8
SUB FAC1_MAN+8,AX
MOV AX,FAC2_MAN+6
SBB FAC1_MAN+6,AX
MOV AX,FAC2_MAN+4
SBB FAC1_MAN+4,AX
MOV AX,FAC2_MAN+2
SBB FAC1_MAN+2,AX
MOV AX,FAC2_MAN
SBB FAC1_MAN,AX
;Set up loop count
MOV CX,80
FDIV1:
;Shift quotient left
SHL FAC3_MAN+8,1
RCL FAC3_MAN+6,1
RCL FAC3_MAN+4,1
RCL FAC3_MAN+2,1
RCL FAC3_MAN,1
;Shift dividend left
SHL FAC1_MAN+8,1
RCL FAC1_MAN+6,1
RCL FAC1_MAN+4,1
RCL FAC1_MAN+2,1
RCL FAC1_MAN,1
;If 1 bit shifted out of dividend (indicating that
; the result of the subtraction was negative,
; restore by adding divisor back in.
JNC FDIV2
MOV AX,FAC2_MAN+8
ADD FAC1_MAN+8,AX
MOV AX,FAC2_MAN+6
ADC FAC1_MAN+6,AX
MOV AX,FAC2_MAN+4
ADC FAC1_MAN+4,AX
MOV AX,FAC2_MAN+2
ADC FAC1_MAN+2,AX
MOV AX,FAC2_MAN
ADC FAC1_MAN,AX
JMP SHORT FDIV3
;If 0 bit shifted out of dividend, continue process, but make
;sure you set a 1 bit in the quotient.
FDIV2: MOV AX,FAC2_MAN+8
SUB FAC1_MAN+8,AX
MOV AX,FAC2_MAN+6
SBB FAC1_MAN+6,AX
MOV AX,FAC2_MAN+4
SBB FAC1_MAN+4,AX
MOV AX,FAC2_MAN+2
SBB FAC1_MAN+2,AX
MOV AX,FAC2_MAN
SBB FAC1_MAN,AX
OR FAC3_MAN+8,1 ;Set lo bit of quotient to 1
FDIV3: LOOP FDIV1
;Move quotient back into FAC1 and normalize
; (borrow the code already in FMUL to do this.)
JMP FMUL3
;
;**********
;NORMALIZE FAC1
;Call this at the end of floating-point operations, and
;before you store the floating-point number.
;
PUBLIC NORM_FAC1
NORM_FAC1:
;If FAC1's mantissa is zero, claer FAC1's exponent and set
;it's sign to 0 to indicate true zero
MOV AX,FAC1_MAN
OR AX,FAC1_MAN+2
OR AX,FAC1_MAN+4
OR AX,FAC1_MAN+6
OR AX,FAC1_MAN+8
JNE NORM0
MOV FAC1_SIGN,AL
MOV FAC1_EXP,AX
XOR BX,BX ;Show all ok
RET
;If we have a 1 in highmost bit - shift FAC1 to the right and
;increment exponent
NORM0: MOV BX,FAC1_EXP
TEST FAC1_MAN,8000H
JZ NORM1
SHR FAC1_MAN,1
RCR FAC1_MAN+2,1
RCR FAC1_MAN+4,1
RCR FAC1_MAN+6,1
RCR FAC1_MAN+8,1
INC BX
;
;Shift FAC1 left until we get a 1 in next-to-highmost bit.
;Subtract 1 from FAC1's exponent each time we do a shift.
NORM1: TEST FAC1_MAN,4000H
JNZ NORM2
SHL FAC1_MAN+8,1
RCL FAC1_MAN+6,1
RCL FAC1_MAN+4,1
RCL FAC1_MAN+2,1
RCL FAC1_MAN,1
DEC BX
JMP NORM1
;
NORM2:
MOV FAC1_EXP,BX
;Check to see if the exponent overflowed
CMP BX,MAXEXP
JB NORM3
MOV BX,EXPOVF ;Error code
RET
;Check to see if the exponent underflowed
NORM3: OR BX,BX
JNE NORM4
MOV BX,EXPUND
RET
NORM4: XOR BX,BX ;Show all ok
RET
;
;Routine to negate the mantissa pointed
;to by BX.
NEGBX:
MOV CX,5
STC
NEG1: NOT WORD PTR 8[BX]
ADC WORD PTR 8[BX],0
DEC BX
DEC BX
LOOP NEG1
RET
;**********
;FLOATING-POINT INPUT ROUTINE
;Assume SI points to a string containing:
; sX.XXXXXEsXXn
; Where s= + or -
; X is 0-9
; n is null
PUBLIC FPINP
FPINP:
;First set the direction for increment
CLD
;Get the sign
LODSB
CMP AL,'+'
JNE INP1
MOV AL,0
JMP SHORT INP2
INP1: MOV AL,80h
INP2: MOV FAC1_SIGN,AL
;Clear CX to hold accumulated value of decimal exponent
XOR CX,CX
;Clear FAC1
MOV FAC1_MAN,CX
MOV FAC1_MAN+2,CX
MOV FAC1_MAN+4,CX
MOV FAC1_MAN+6,CX
MOV FAC1_MAN+8,CX
;Get the digit to the left of the decimal point and increment
;decimal exponent
LODSB
CALL ADDIGIT
;Skip past the decimal point.
;**NOTE: Add code to verify format here.
LODSB
;Read digits to the right of the decimal point
INP3: LODSB
CMP AL,'0'
JL INP4 ;Jump if not digit (must be 'E')
CMP AL,'9'
JG INP4 ;Jump if not digit (must be 'E')
CALL ADDIGIT
DEC CX ;Decr. decimal accumulator
JMP INP3
;Skip past the 'E'.
;**NOTE: Add code to verify format here.
INP4:
;Read in exponent sign.
LODSB
CMP AL,'+'
JNE INP5
MOV AL,0 ;Must be negative sign
JMP SHORT INP6
INP5: MOV AL,80H
INP6: MOV EXP_SIGN,AL
;Read in exponent digits
XOR BX,BX ;Use as accumulator
XOR AX,AX
MOV DL,10 ;Multiplier
INP7: LODSB
CMP AL,'0'
JL INP8 ;Jump if not digit (must be null)
CMP AL,'9'
JG INP8 ;Jump if not digit (must be goofup)
SUB AL,'0' ;Make it a true number
XCHG BX,AX ;Accumulated value in AX
MUL DL
ADD AX,BX ;Add in digit
XCHG BX,AX ;Accumulated value back in BX
JMP INP7
;Fix up the accumulated exponent in BX
INP8: MOV AL,EXP_SIGN
OR AL,AL
JNE INP9
;Exponent sign is positive
ADD CX,BX
JMP SHORT INP9A
;Exponent sign is negative
INP9: SUB CX,BX
;Normalize FAC1
INP9A: MOV FAC1_EXP,BIAS+79
CALL NORM_FAC1
;Load FAC2 with a floating point 10.0
XOR AX,AX
MOV FAC2_SIGN,AL
MOV FAC2_EXP,BIAS+4
MOV FAC2_MAN,5000H
MOV FAC2_MAN+2,AX
MOV FAC2_MAN+4,AX
MOV FAC2_MAN+6,AX
MOV FAC2_MAN+8,AX
;Examine the contents of CX (our decimal exponent). If CX=-n, divide
;FAC1 by 10 n times. If CX=+n, multiply FAC1 by n.
MOV DX,CX
OR DX,DX
JE INP11 ;Jump if nothing to do.
JL INP12
;Multiply
INP10: CALL FPMULT
DEC DX
JNE INP10
INP11: RET
;Divide
INP12: NEG DX
INP13: CALL FPDIV
DEC DX
JNE INP13
RET
;
ADDIGIT:
CALL FAC1_MBY10 ;Multiply mantissa by 10
;add in the digit
AND AX,0FH
ADD FAC1_MAN+8,AX
ADC FAC1_MAN+6,0
ADC FAC1_MAN+4,0
ADC FAC1_MAN+2,0
ADC FAC1_MAN,0
RET
;**********
;Floating-point output routine
;Stores the number in FAC1 into memory location
;pointed to by DI. CX contains the number of digits
;to the right of the decimal place to output.
;Number is stored as a string:
; sd.dddddddEsddd<Null>
; s is + or - and d is a digit.
;Note: the mantissa can hold 19 significant digits.
PUBLIC FPOUT
FPOUT:
CLD
PUSH CX ;Save
;First see if we have true zero. If so we can skip
;a lot.
CMP FAC1_EXP,0
JZ FOUT3
;Put a floating-point 10 in FAC2. We'll use it later
XOR AX,AX
MOV FAC2_SIGN,AL
MOV FAC2_MAN+2,AX
MOV FAC2_MAN+4,AX
MOV FAC2_MAN+6,AX
MOV FAC2_MAN+8,AX
MOV FAC2_MAN,5000H
MOV FAC2_EXP,BIAS+4
;Use DX to hold the decimal exponent
XOR DX,DX
;Divide by 10 while FAC1 is >15 (Check for this by watching
;the exponent). For each division, increment DX by 1.
FOUT1: CMP FAC1_EXP,BIAS+4
JBE FOUT2
CALL FPDIV
INC DX
JMP FOUT1
;Multiply by 10 while FAC1 is <1. For each multiplication,
;decrement DX by 1.
FOUT2: CMP FAC1_EXP,BIAS+1
JA FOUT2A
CALL FPMULT
DEC DX
JMP FOUT2
;If number is greater than or equal to 10, do one more
;division by 10 to get things in line. And don't forget to
;increment DX.
FOUT2A:
CMP FAC1_EXP,BIAS+4
JNE FOUT3
CMP FAC1_MAN,5000H
JL FOUT3
CALL FPDIV
INC DX
;Position binary point between bits 75 and 76 so we can
;isolate the integer portion of the floating point number.
FOUT3: CMP FAC1_EXP,BIAS+4
JNE FOUT4
SHL FAC1_MAN+8,1
RCL FAC1_MAN+6,1
RCL FAC1_MAN+4,1
RCL FAC1_MAN+2,1
RCL FAC1_MAN,1
JMP SHORT FOUT5
FOUT4: CMP FAC1_EXP,BIAS+3
JGE FOUT4A
SHR FAC1_MAN,1
RCR FAC1_MAN+2,1
RCR FAC1_MAN+4,1
RCR FAC1_MAN+6,1
RCR FAC1_MAN+8,1
INC FAC1_EXP
JMP FOUT4
;Do any rounding here. (I'm not happy with what I've got here...
;perhaps a better rounding method could be found.)
FOUT4A:
ADD FAC1_MAN+8,9
ADC FAC1_MAN+6,0
ADC FAC1_MAN+4,0
ADC FAC1_MAN+2,0
ADC FAC1_MAN,0
;Make sure we didn't overflow
CMP FAC1_MAN,0A000H
JB FOUT5
XOR AX,AX
MOV FAC1_MAN+8,AX
MOV FAC1_MAN+6,AX
MOV FAC1_MAN+4,AX
MOV FAC1_MAN+2,AX
MOV FAC1_MAN,1000H
INC DX
;Save the sign
FOUT5: CMP FAC1_SIGN,0
JNE FOUT5A
MOV BYTE PTR [DI],'+'
JMP SHORT FOUT5B
FOUT5A: MOV BYTE PTR [DI],'-'
FOUT5B: INC DI
;Get leftmost digit
MOV AL,BYTE PTR FAC1_MAN+1
SHR AL,1
SHR AL,1
SHR AL,1
SHR AL,1 ;Digit now in low nibble
ADD AL,'0'
STOSB
;OUTPUT the decimal point
MOV AL,'.'
STOSB
;Output the remaining digits.
POP CX ;Restore saved count
FOUT6: AND FAC1_MAN,0FFFH ;Strip integer portion
CALL FAC1_MBY10 ;Multiply by 10
MOV AL,BYTE PTR FAC1_MAN+1
SHR AL,1 ;Get next integer part
SHR AL,1
SHR AL,1
SHR AL,1
ADD AL,'0'
STOSB
LOOP FOUT6
;Get the exponent portion
MOV AL,'E'
STOSB
OR DX,DX
JS FOUT7
MOV AL,'+'
JMP SHORT FOUT8
FOUT7: MOV AL,'-'
NEG DX ;Absolute value of exp. in DX
FOUT8: STOSB
;Following routine gets the exponent value. Note that we use
;division by 10 to strip off digits as remainders. This means
;the number comes out in right-to-left order...backwards. So
;we have to 'flip' the number when we're done.
MOV AX,DX
OR AX,AX ;0 exponent?
JZ FOUT10
MOV SI,DI ;Save location in buffer
MOV CX,10
FOUT9: XOR DX,DX ;Clear for division
DIV CX
FOUT10: ADD DL,'0' ;Digit in DX
MOV [DI],DL
INC DI
OR AX,AX ;Done?
JNE FOUT9
MOV BYTE PTR [DI],0 ;Store null
;Fix the exponent so it reads left-to-right.
DEC DI
FOUT11: CMP SI,DI ;Finished flipping?
JAE FOUT12
MOV AL,[DI] ;Swap
XCHG AL,[SI]
MOV [DI],AL
INC SI
DEC DI
JMP FOUT11
;Exit
FOUT12: RET
;**********
;Multiply contents of FAC1_MAN by 10.
;Do this using the formula: 10*x = 2*x + 8*x and
;the fact that you can multiply by 2 and multiply by
;8 by doing left shifts.
;Note that this only multiplies manissas...not the entire FP number.
FAC1_MBY10:
PUSH AX
SHL FAC1_MAN+8,1
RCL FAC1_MAN+6,1
RCL FAC1_MAN+4,1
RCL FAC1_MAN+2,1
RCL FAC1_MAN,1
;
MOV AX,FAC1_MAN
MOV FAC3_MAN,AX
MOV AX,FAC1_MAN+2
MOV FAC3_MAN+2,AX
MOV AX,FAC1_MAN+4
MOV FAC3_MAN+4,AX
MOV AX,FAC1_MAN+6
MOV FAC3_MAN+6,AX
MOV AX,FAC1_MAN+8
MOV FAC3_MAN+8,AX
;
SHL FAC1_MAN+8,1
RCL FAC1_MAN+6,1
RCL FAC1_MAN+4,1
RCL FAC1_MAN+2,1
RCL FAC1_MAN,1
;
SHL FAC1_MAN+8,1
RCL FAC1_MAN+6,1
RCL FAC1_MAN+4,1
RCL FAC1_MAN+2,1
RCL FAC1_MAN,1
;
MOV AX,FAC3_MAN+8
ADD FAC1_MAN+8,AX
MOV AX,FAC3_MAN+6
ADC FAC1_MAN+6,AX
MOV AX,FAC3_MAN+4
ADC FAC1_MAN+4,AX
MOV AX,FAC3_MAN+2
ADC FAC1_MAN+2,AX
MOV AX,FAC3_MAN
ADC FAC1_MAN,AX
POP AX
;
RET
#endasm